package hyl.base.net.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import hyl.base.net.IData1;
import hyl.base.net.IpgReader;
import hyl.base.net.ISocketCli;
import hyl.core.MyDate;
import hyl.core.MyFun;
import hyl.core.run.IDo1;
import hyl.core.run.MyRun;

/*服务器端，:接收客户端发送过来的数据并显示，
 *服务器把上接收到的数据加上"echo from service:"再发送回去*/
public class NIOSocketCli extends NIOSocket implements ISocketCli {

	/* 服务器地址 */

	SocketChannel _client = null;
	Selector selector;
	String saddr = null;

	public NIOSocketCli(InetSocketAddress sa) {
		_sa = sa;

	}

	public NIOSocketCli(String ip, int port) {
		_sa = new InetSocketAddress(ip, port);

	}

	public NIOSocketCli(String 服务端ip, int port, IDo1<IpgReader> 收到处理函数) {
		this(服务端ip, port);
		_IDo接收处理函数 = 收到处理函数;
	}

	// 单例 防止多线程 重复启动
	NIOReader _reader = null;
	Object lockobj = new Object();// 未连接时不允许 启动接收
	InetSocketAddress _sa;

	public void connOne() throws IOException {
		reconn();
		MyFun.print("与服务器的连接成功!!", _sa, _client.getLocalAddress());
	}

	public void conn() {
		MyRun.start用户线程(() -> {		
			for(int i=0;i<3;i++) {
				try {
					reconn();
					MyFun.print("连接成功!!", _sa, _client.getLocalAddress());
					break;
				} catch (IOException e1) {
					MyFun.print("无法连接,等待3s后重新尝试");
					MyDate.sleeps(3);
				}
			}

		});
	}

	public void reconn() throws IOException {

		/* 创建TCP通道 */
		_client = SocketChannel.open();
		/* 设置通道为非阻塞 */
		_client.configureBlocking(false);
		/* 向服务器发起连接,一个通道代表一条tcp链接 */
		_client.connect(_sa);
		/* 等待三次握手完成 */
		// MyFun.print( "与服务器的连接", sa);
		while (!_client.finishConnect()) {
			// MyFun.printn(1, "暂时无法连接服务器", sa);
			MyDate.sleeps(1);
		}

		synchronized (lockobj) {
			lockobj.notify();
		}
	}

	void 阻塞等待连接() {
		if (!isConnected()) {
			// o1.get();
			synchronized (lockobj) {
				try {
					System.out.println("等待连接成功...");
					lockobj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	Thread _thread;

	// 不开启接收的情况下 只能发送
	public IpgReader startReader() {
		if (_reader != null)
			return _reader;
		阻塞等待连接();
		/* 向选择器注册读通道 实现全双工 */
		_reader = new NIOReader(_client, _IDo接收处理函数);
		/* 注册感兴趣事件 */
		try {
			/* 创建选择器 */
			selector = Selector.open();
			_client.register(selector, SelectionKey.OP_READ, _reader);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		_thread = MyRun.start用户线程(() -> {
			/* 与服务器断开或线程被中断则结束线程 */
			try {
				//int i = 0;
				while (!Thread.currentThread().isInterrupted()) {
					/* 阻塞等待 */
					selector.select();

					/* Set中的每个key代表一个通道 */
					Set<SelectionKey> keySet = selector.selectedKeys();
					Iterator<SelectionKey> it = keySet.iterator();
					/**
					 * 对于客户端 只会连接一个服务端,只会有一个 SelectionKey; <br>
					 * 如果上一个key 断开 会再次循环; 如果没有断开 ,不会循环;
					 */
//					if (i > 1) {
//						return;
//					}
					//MyFun.print(i);
					//i++;
					/* 遍历每个已就绪的通道，处理这个通道已就绪的事件 */
					while (it.hasNext()) {

						SelectionKey key = it.next();
						// MyFun.print(key.isReadable(), i);
//						SocketChannel sc = (SocketChannel) key.channel();
//						if (!sc.isConnected())
//							return;
						/* 防止下次select方法返回已处理过的通道 */
						it.remove();
						/* 表示底层socket的读缓冲区有数据可读 */
						if (key.isReadable()) {
							read一次(key);
						}

					}
				}
			} catch (IOException e) {
				System.out.println(" 连接错误,已关闭");
			} finally {
				close();
			}
		});

		return _reader;
	}

	/*
	 * 使用回调函数处理 返回结果 无意义 已经有接收处理函数了,再写一个就是多余
	 * 
	 * @param 回调
	 */
//	public void getResult(IDo1<byte[]> 回调) {
//		IReader rd = getReader();
//		byte[] data = rd.pullData();
//		回调.run(data);
//	}
//
//	public byte[] getResult() {
//		IReader rd = getReader();
//		return rd.pullData();
//
//	}

	/**
	 * 抛出异常的目的 是 识别网络断开
	 */
	public boolean send(byte[] 数据包) {
		return send(_client, IData1.B类型_数据, 数据包);
	}

	public boolean send(byte 类型, byte[] 数据包) {
		return send(_client, 类型, 数据包);
	}

	@Override
	public void close() {
		if (_IDo断开处理函数 != null)
			_IDo断开处理函数.run(_reader);
		try {
			selector.close();
			_client.close();
			MyFun.print(" 连接已断开");
		} catch (IOException e1) {
		}

	}

	public boolean isConnected() {
		return _client == null || _client.isConnected();
	}
}
